package com.study.zroutercompiler;

import com.zrouter.annotation.Route;

import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedOptions({"moduleName"})
public class ZRouterCompiler extends AbstractProcessor {

    private Messager messager;//打印日志使用
    private String moduleName;//各个组件所在的模块名
    private Filer filer;//java文件生成相关类
    private final Map<String, String> routeMap = new HashMap<>();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        moduleName = processingEnv.getOptions().get("moduleName");
        filer = processingEnv.getFiler();
        log("组件所在的包：" + moduleName);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        //set为空，说明项目中没有找到被@Route注解的类
        if (set.isEmpty()) {
            return false;
        }
        routeMap.clear();

        //获取被@Route注解的元素
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Route.class);
        for (Element element : elements) {
            TypeElement typeElement = (TypeElement) element;
            //获取被注解的类的全类名
            String className = typeElement.getQualifiedName().toString();
            String path = typeElement.getAnnotation(Route.class).value();
            log("被注解的类:" + className);
            routeMap.put(path, className);
        }
        createFile();
        return true;
    }

    /**
     * 创建类
     */
    private void createFile() {
        String className = "ZRouter$$" + moduleName;
        Writer writer = null;
        try {
            JavaFileObject javaFile = filer.createSourceFile("com.z_router.map." + className);
            writer = javaFile.openWriter();
            String routes = getRegisterRoute();
            writer.write(
                    "package com.z_router.map;\n" +
                            "\n" +
                            "import java.util.Map;\n" +
                            "import com.study.zrouter.IRouter;\n" +
                            "\n" +
                            "public class " + className + " implements IRouter {\n" +
                            "    @Override\n" +
                            "    public void register(Map<String, String> routeMap) {\n"
                            + routes +
                            "    }\n" +
                            "}"
            );

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取注册的路由信息
     *
     * @return 返回拼装后的java代码
     */
    private String getRegisterRoute() {
        StringBuilder builder = new StringBuilder();
        routeMap.forEach((key, value) -> builder.append("        routeMap.put(").append("\"").append(key).append("\"").append(", ").append("\"").append(value).append("\"").append(");\n"));
        return builder.toString();
    }

    /**
     * 添加支持的注解类型
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(Route.class.getCanonicalName());
        return annotations;
    }

    /**
     * 编译时打印log数据
     */
    private void log(String msg) {
        messager.printMessage(Diagnostic.Kind.NOTE, msg);
    }
}